/*
 * 2006-03-10 lxm 创建。 
 */
package com.hd123.rumba.util;

import java.io.IOException;
import java.io.Serializable;
import java.util.StringTokenizer;

/**
 * 版本号
 * <p>
 * 
 * 抽象数据类型。持久化的时候转换为一个字符串，对应一个字符串类型数据字段。
 * 
 * @author lxm
 * 
 */
public class Version implements Serializable {

  /** Macro Version */
  private Integer macroVersion;

  public Integer getMacroVersion() {
    return macroVersion;
  }

  /** Minor Version */
  private Integer minorVersion;

  public Integer getMinorVersion() {
    return minorVersion;
  }

  /** Release */
  private Integer release;

  public Integer getRelease() {
    return release;
  }

  /** Build No */
  private Integer buildNo;

  public Integer getBuildNo() {
    return buildNo;
  }
  
  private Version() {
    super();
  }

  /**
   * 构造函数
   * 
   * @param macroVer
   * @param minorVer
   */
  public Version(int macroVer, int minorVer) {
    super();
    this.macroVersion = new Integer(macroVer);
    this.minorVersion = new Integer(minorVer);
    this.release = null;
    this.buildNo = null;
  }

  /**
   * 构造函数
   * 
   * @param macroVer
   * @param minorVer
   * @param release
   */
  public Version(int macroVer, int minorVer, int release) {
    super();
    this.macroVersion = new Integer(macroVer);
    this.minorVersion = new Integer(minorVer);
    this.release = new Integer(release);
    this.buildNo = null;
  }

  /**
   * 构造函数
   * 
   * @param macroVer
   * @param minorVer
   * @param release
   * @param buildNo
   */
  public Version(int macroVer, int minorVer, int release, int buildNo) {
    super();
    this.macroVersion = new Integer(macroVer);
    this.minorVersion = new Integer(minorVer);
    this.release = new Integer(release);
    this.buildNo = new Integer(buildNo);
  }

  /**
   * 构造函数。
   * 
   * @param verStr
   *          指定版本号的字符串表示。例如："2.0.1.3024"。
   */
  public Version(String verStr) {
    super();
    parseStr(verStr);
  }

  public Version clone() {
    return new Version(this.toString());
  }

  /**
   * 将当前版本号与指定版本号比较是否相等。
   * 
   * @param version
   *          指定版本号。
   * @return 是否相等。
   */
  public boolean equals(Version version) {
    return this.toString().equals(version.toString());
  }

  public String toString() {
    String str = macroVersion.toString();
    if (minorVersion != null)
      str = str + "." + minorVersion.toString();
    if (release != null)
      str = str + "." + release.toString();
    if (buildNo != null)
      str = str + "." + buildNo.toString();
    return str;
  }

  /**
   * 将当前版本号与指定版本号进行比较，返回比较结果。
   * 
   * @param version
   *          指定参与比较的版本号。
   * @return 如果当前版本号比指定版本号大，返回一个正整数；如果比指定版本号小，返回一个负整数；如果相等则返回0。
   */
  public int compareTo(Version version) {
    int result = this.macroVersion.compareTo(version.getMacroVersion());
    if (result != 0)
      return result;

    result = this.minorVersion.compareTo(version.getMinorVersion());
    if (result != 0)
      return result;

    if (this.release != null && version.getRelease() == null)
      return 1;
    else if (this.release == null && version.getRelease() != null)
      return -1;
    else if (this.release == null && version.getRelease() == null)
      result = 0;
    else
      result = this.release.compareTo(version.getRelease());
    if (result != 0)
      return result;

    if (this.buildNo != null && version.getBuildNo() == null)
      return 1;
    else if (this.buildNo == null && version.getBuildNo() != null)
      return -1;
    else if (this.buildNo == null && version.getBuildNo() == null)
      result = 0;
    else
      result = this.buildNo.compareTo(version.getBuildNo());
    return result;
  }

  /**
   * 版本号相加运算
   * <p>
   * 
   * 例如以下程序实现版本号运算"1.0.3 + 0.0.1 = 1.0.4"：<br>
   * <code>
   * Version oldVer = new Version("1.0.3");<br>
   * Version newVer = oldVer.add(new Version("0.0.1"));
   * </code>
   * 
   * @param augend
   * @return
   */
  public Version add(Version augend) {
    int macroVer = this.macroVersion.intValue() + augend.getMacroVersion().intValue();
    int minorVer = this.minorVersion.intValue() + augend.getMinorVersion().intValue();
    Version result = new Version(macroVer, minorVer);

    if (this.release == null && augend.getRelease() == null)
      result.release = null;
    else {
      int rel = (this.release == null ? 0 : this.release.intValue())
          + (augend.getRelease() == null ? 0 : augend.getRelease().intValue());
      result.release = new Integer(rel);

      if (this.buildNo == null && augend.getBuildNo() == null)
        result.buildNo = null;
      else {
        int bn = (this.buildNo == null ? 0 : this.buildNo.intValue())
            + (augend.getBuildNo() == null ? 0 : augend.getBuildNo().intValue());
        result.buildNo = new Integer(bn);
      }
    }
    return result;
  }

  /**
   * 版本号截取运算。
   * 
   * @param precision
   *          指定截取到第几位，第1位指的是macroVersion。如果精度非法，则不做任何截取运算。
   * @return 返回运算结果对象。
   */
  public Version truncate(int precision) {
    if (precision <= 0)
      return clone();
    Version result = new Version();
    if (precision >= 1 && macroVersion != null)
      result.macroVersion = macroVersion;
    if (precision >= 2 && minorVersion != null)
      result.minorVersion = minorVersion;
    if (precision >= 3 && release != null)
      result.release = release;
    if (precision >= 4 && buildNo != null)
      result.buildNo = buildNo;
    return result;
  }

  private void parseStr(String verStr) {
    StringTokenizer st = new StringTokenizer(verStr, ".");
    this.macroVersion = new Integer(st.nextToken());
    this.minorVersion = null;
    this.release = null;
    this.buildNo = null;
    if (st.hasMoreTokens()) {
      this.minorVersion = new Integer(st.nextToken());
      if (st.hasMoreTokens()) {
        this.release = new Integer(st.nextToken());
        if (st.hasMoreTokens()) {
          this.buildNo = new Integer(st.nextToken());
        }
      }
    }
  }

  private void writeObject(java.io.ObjectOutputStream out) throws IOException {
    out.writeBytes(this.toString());
  }

  private void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException {
    byte[] buf = new byte[in.available()];
    in.readFully(buf);
    parseStr(new String(buf));
  }

}
